// Created August 30, 2007
//
// File: functional.js
// Author: Nate Young
//

// returns a subsequence of a sequence starting at from
//
function slice(from, seq) {
    var arr = new Array();
    for(var i = from; i < seq.length; i++) {
        arr.push(seq[i]);
    }
    return arr;
}

// returns the length of the shortest of a list of sequences
//
function shortestof(seqs) {
    var shortest = seqs[0].length;
    for(var i = 0; i < seqs.length; i++) {
        if(seqs[i].length < shortest)
            shortest = seqs[i].length;
    }
    return shortest;
}

// takes a list of sequences and returns the index-th 
// element of each in a list
//
// index - element of each sequence to collect
// seqs - list of sequences to collect elements from
//
function collectargs(index, seqs) {
    var collected = new Array();
    for(var i = 0; i < seqs.length; i++)
        collected.push(seqs[i][index]);
    return collected;
}

// applies a function to elements of given sequences and returns 
// a list of the results
//
// this method takes at least 2 arguments, fn and seq
// and can take more. fn will be supplied with one element
// from each given sequence
//
// fn - the function to call
// seq - sequence to supply fn with arguments
//
function map(fn, seq) {
    var seqs = slice(1, arguments);

    var mapped = new Array();
    var args = false;

    var i = 0;
    var shortest = shortestof(seqs);
    while(shortest > i) {
        args = collectargs(i, seqs);
        mapped.push(fn.apply(null, args));

        i++;
    }

    return mapped;
}

// return a list of elements that satisfy a given function
//
// filter will pass each element of the given sequence to 
// the supplied function and return a list of the elements
// for which the function returned true
//
// fn - function taking a single argument
// 
function filter(fn, seq) {
    var filtered = new Array();

    for(var i = 0; i < seq.length; i++) {
        if(fn(seq[i])) {
            filtered.push(seq[i]);
        }
    }

    return filtered;
}

// return a list of elements in a list of lists that satisfy a given function
//
// rfilter differs from filter in that it recursively descends
// into nested lists to return a flat lists of elements.
//
// fn - function taking a single argument
function rfilter(fn, seq) {
    var filtered = new Array();

    for(var i = 0; i < seq.length; i++) {
        if(seq[i] && seq[i].length) {  //element is a sequence
            filtered = filtered.concat(rfilter(fn, seq[i]));
        } else {
            if(fn(seq[i])) {
                filtered.push(seq[i]);
            }
        }
    }

    return filtered;
}
